//! arm 虚拟板卡
//!
//! ```text
//! aarch64 虚拟板卡实现如下设备:
//!                 cpu -> cortex-a57 (smp = 4)
//!     clk-source
//!                     gicv2
//!                     pl011
//! ```
//! 如上, 足够工作了

use std::{
    net::SocketAddr,
    sync::{Arc, Mutex},
};

use boardinfo::{
    dfs::{
        dfs_read,
        params::{ParamCreateOptions, ParamOperation},
    },
    register_board, BoardArch, BoardInfoImpl,
};
use chardev::{stdio::chardevice_stdio_map, telnet::chardevice_telnet_map, CharBackendDevice};
use clock::{root_clock, HwClockBuilder, HwClockInput};
use pl011::Pl011StateBuilder;

struct BoardInfoAarch64Virt;

impl BoardInfoImpl for BoardInfoAarch64Virt {
    fn arch(&self) -> BoardArch {
        BoardArch::AArch64
    }

    fn name(&self) -> &'static str {
        "virt"
    }

    fn info(&self) -> &'static str {
        "test"
    }

    fn info_long(&self) -> &'static str {
        "test long"
    }

    fn pre_config(&self, path: Arc<boardinfo::dfs::inode::DfsInode>) -> boardinfo::BInfoResult<()> {
        ParamCreateOptions::new()
            .read_write(true)
            .name("pl011_mon")
            .create_param_file(
                path,
                Arc::new(Pl011Mon { target: Mutex::new(String::from("stdio")) }),
            )
            .unwrap();

        Ok(())
    }

    fn pre_config_end(
        &self,
        path: Arc<boardinfo::dfs::inode::DfsInode>,
    ) -> boardinfo::BInfoResult<()> {
        path.set_no_write("pl011_mon")
    }

    fn create_board(&self) -> boardinfo::BInfoResult<()> {
        let mut buf = String::new();
        dfs_read("/board/pre_config/pl011_mon", &mut buf).unwrap();
        let char_dev_impl = if buf.eq("stdio") {
            chardevice_stdio_map().char_device_impl()
        } else {
            let sockaddr: SocketAddr = buf.parse().unwrap();
            chardevice_telnet_map().create_session(sockaddr);
            chardevice_telnet_map().char_backend_map(sockaddr).unwrap().char_device_impl()
        };
        let char_backend_device = Arc::new(CharBackendDevice::create(char_dev_impl));

        let clock_input = Arc::new(HwClockInput::default());

        let pl011 = Arc::new(
            Pl011StateBuilder::builder()
                .clk_input(clock_input.clone())
                .name("virt_pl011")
                .set_char_backend_device(char_backend_device)
                .type_pl011()
                // .set_irq(irq)
                .build(),
        );

        let root_clock = root_clock();
        let clock =
            Arc::new(HwClockBuilder::builder().clock_input(clock_input).clock_event(pl011).build());
        root_clock.clock_set_child(clock);

        Ok(())
    }
}

struct Pl011Mon {
    target: Mutex<String>,
}

impl ParamOperation for Pl011Mon {
    fn read(&self) -> String {
        self.target.lock().unwrap().clone()
    }

    fn wrtie(&self, buf: &str) -> boardinfo::dfs::DfsResult<()> {
        let mut lock = self.target.lock().unwrap();
        if buf.eq("stdio") {
            *lock = String::from("stdio");
        } else {
            let _: SocketAddr = buf.parse().map_err(|_| boardinfo::dfs::DfsError::InvalidArgs)?;
            *lock = String::from(buf);
        }
        Ok(())
    }
}

/// 注册 aarch64 virt 板卡
pub fn boardinfo_aarch64_virt() {
    register_board(Box::new(BoardInfoAarch64Virt));
}
